﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;

namespace daSyntaxAnalyser.Analysers
{
    // Объекты и анализаторы
    using daBase.Objects;
    using daBase.Objects.Safe;
    using daBase.Analyser;
    using DfmAnalyser;

    /// <summary>
    /// Лексический анализатор
    /// </summary>
    [AnalyserDefaultSettings("Лексический анализатор", "Неизвестная последовательность языка!", 100, "Анализатор не в состоянии распознать данную символьную последовательность. Возможно она некорректна или анализатор работает неправильно. Сообщите о данной ошибке разработчику.")]
    public class LexicalAnalyser : LayerAnalyser
    {
        // Одиночка
        private static LexicalAnalyser instance;

        // Текуший анализ
        private string source, currentId;
        private int index;
        private DelphiTokens token;
        private FileMap filemap;
        public static Dictionary<String, int> FileSLOC;
        public static Dictionary<String, int> FileEmptyLiles;
        private List<TokenInfo> tokens;
        private SafeDictionary<string, string> metatags;
        private List<string> dfmids;

        public FileMap FileMap { get { return filemap; } }
        //public List<FileMap> FileMaps { get { return filemaps; } }

        /// <summary>
        /// Сообщение "Файл проанализирован"
        /// </summary>
        public event EventHandler<SourceArgs> FileAnalysedEvent;

        // регулярные выражения для выявления подключения dfm и мета тегов
        private static Regex dfm = new Regex("^{\\$R (?<dfm>.*)\\.dfm}$", RegexOptions.IgnoreCase);
        private static Regex meta = new Regex("#(?<tag>\\w+)\\s+(?<value>[^\\r\\n)}*]+)\\s*", RegexOptions.IgnoreCase);

        // Наборы токенов
        // keywords - ключевые слова
        // keysymbols - ключевые "не слова" (:= , [ @ ^ etc)
        // canbeid - токен, который может быть как ключевым словом так и идентификатором
        // extraid - токен, который не может быть объявлен как Id, но это не мешает его использованию в качестве Id
        private Dictionary<string, DelphiTokens> keywords, keysymbols;


        /// <summary>
        /// Инициализация ключевых слов
        /// </summary>
        private void InitKeyWords()
        {
            if (keywords == null)
            {
                keywords = new Dictionary<string, DelphiTokens>();
                keywords.Add("program", DelphiTokens.Program);
                keywords.Add("library", DelphiTokens.Library);
                keywords.Add("unit", DelphiTokens.Unit);
                keywords.Add("initialization", DelphiTokens.Initialization);
                keywords.Add("implementation", DelphiTokens.Implementation);
                keywords.Add("finalization", DelphiTokens.Finalization);
                keywords.Add("uses", DelphiTokens.Uses);
                keywords.Add("exports", DelphiTokens.Exports);
                keywords.Add("type", DelphiTokens.Type);
                keywords.Add("var", DelphiTokens.Var);
                keywords.Add("const", DelphiTokens.Const);
                keywords.Add("resourcestring", DelphiTokens.Resourcestring);
                keywords.Add("threadvar", DelphiTokens.Threadvar);
                keywords.Add("interface", DelphiTokens.Interface);
                keywords.Add("dispinterface", DelphiTokens.Dispinterface);
                keywords.Add("class", DelphiTokens.Class);
                keywords.Add("object", DelphiTokens.Object);
                keywords.Add("record", DelphiTokens.Record);
                keywords.Add("set", DelphiTokens.Set);
                keywords.Add("array", DelphiTokens.Array);
                keywords.Add("file", DelphiTokens.File);
                keywords.Add("abstract", DelphiTokens.Abstract);
                keywords.Add("sealed", DelphiTokens.Sealed);
                keywords.Add("procedure", DelphiTokens.Procedure);
                keywords.Add("function", DelphiTokens.Function);
                keywords.Add("property", DelphiTokens.Property);
                keywords.Add("operator", DelphiTokens.Operator);
                keywords.Add("constructor", DelphiTokens.Constructor);
                keywords.Add("destructor", DelphiTokens.Destructor);
                keywords.Add("readonly", DelphiTokens.ReadOnly);
                keywords.Add("dispid", DelphiTokens.Dispid);
                keywords.Add("platform", DelphiTokens.Platform);
                keywords.Add("absolute", DelphiTokens.Absolute);
                keywords.Add("packed", DelphiTokens.Packed);
                keywords.Add("default", DelphiTokens.Default);
                keywords.Add("nodefault", DelphiTokens.Nodefault);
                keywords.Add("implements", DelphiTokens.Implements);
                keywords.Add("reference", DelphiTokens.Reference);
                keywords.Add("private", DelphiTokens.Private);
                keywords.Add("public", DelphiTokens.Public);
                keywords.Add("published", DelphiTokens.Published);
                keywords.Add("protected", DelphiTokens.Protected);
                keywords.Add("strict", DelphiTokens.Strict);
                keywords.Add("virtual", DelphiTokens.Virtual);
                keywords.Add("override", DelphiTokens.Override);
                keywords.Add("overload", DelphiTokens.Overload);
                keywords.Add("static", DelphiTokens.Static);
                keywords.Add("safecall", DelphiTokens.Safecall);
                keywords.Add("stdcall", DelphiTokens.Stdcall);
                keywords.Add("dynamic", DelphiTokens.Dynamic);
                keywords.Add("forward", DelphiTokens.Forward);
                keywords.Add("far", DelphiTokens.Far);
                keywords.Add("reintroduce", DelphiTokens.Reintroduce);
                keywords.Add("register", DelphiTokens.Register);
                keywords.Add("stored", DelphiTokens.Stored);
                keywords.Add("automated", DelphiTokens.Automated);
                keywords.Add("cdecl", DelphiTokens.Cdecl);
                keywords.Add("varargs", DelphiTokens.Varargs);
                keywords.Add("message", DelphiTokens.Message);
                keywords.Add("assembler", DelphiTokens.Assembler);
                keywords.Add("name", DelphiTokens.Name);
                keywords.Add("index", DelphiTokens.Index);
                keywords.Add("near", DelphiTokens.Near);
                keywords.Add("export", DelphiTokens.Export);
                keywords.Add("local", DelphiTokens.Local);
                keywords.Add("external", DelphiTokens.External);
                keywords.Add("resident", DelphiTokens.Resident);
                keywords.Add("in", DelphiTokens.In);
                keywords.Add("out", DelphiTokens.Out);
                keywords.Add("read", DelphiTokens.Read);
                keywords.Add("write", DelphiTokens.Write);
                keywords.Add("asm", DelphiTokens.Asm);
                keywords.Add("begin", DelphiTokens.Begin);
                keywords.Add("end", DelphiTokens.End);
                keywords.Add("if", DelphiTokens.If);
                keywords.Add("then", DelphiTokens.Then);
                keywords.Add("else", DelphiTokens.Else);
                keywords.Add("case", DelphiTokens.Case);
                keywords.Add("of", DelphiTokens.Of);
                keywords.Add("while", DelphiTokens.While);
                keywords.Add("for", DelphiTokens.For);
                keywords.Add("to", DelphiTokens.To);
                keywords.Add("downto", DelphiTokens.Downto);
                keywords.Add("do", DelphiTokens.Do);
                keywords.Add("until", DelphiTokens.Until);
                keywords.Add("repeat", DelphiTokens.Repeat);
                keywords.Add("try", DelphiTokens.Try);
                keywords.Add("except", DelphiTokens.Except);
                keywords.Add("finally", DelphiTokens.Finally);
                keywords.Add("goto", DelphiTokens.Goto);
                keywords.Add("label", DelphiTokens.Label);
                keywords.Add("with", DelphiTokens.With);
                keywords.Add("as", DelphiTokens.As);
                keywords.Add("is", DelphiTokens.Is);
                keywords.Add("at", DelphiTokens.At);
                keywords.Add("on", DelphiTokens.On);
                keywords.Add("raise", DelphiTokens.Raise);
                keywords.Add("not", DelphiTokens.Not);
                keywords.Add("and", DelphiTokens.And);
                keywords.Add("xor", DelphiTokens.Xor);
                keywords.Add("or", DelphiTokens.Or);
                keywords.Add("shl", DelphiTokens.Shl);
                keywords.Add("shr", DelphiTokens.Shr);
                keywords.Add("div", DelphiTokens.Div);
                keywords.Add("mod", DelphiTokens.Mod);
                keywords.Add("true", DelphiTokens.True);
                keywords.Add("false", DelphiTokens.False);
                keywords.Add("inherited", DelphiTokens.Inherited);
            }
        }

        /// <summary>
        /// Инициализация ключевых "не слов"
        /// </summary>
        private void InitKeySymbols()
        {
            if (keysymbols == null)
            {
                keysymbols = new Dictionary<string, DelphiTokens>();
                keysymbols.Add("+", DelphiTokens.Addition);
                keysymbols.Add("*", DelphiTokens.Multiply);
                keysymbols.Add("-", DelphiTokens.Subtraction);
                keysymbols.Add("/", DelphiTokens.Division);
                keysymbols.Add("=", DelphiTokens.Equal);
                keysymbols.Add("<", DelphiTokens.Less);
                keysymbols.Add(">", DelphiTokens.Greater);
                keysymbols.Add("<=", DelphiTokens.LessOrEqual);
                keysymbols.Add(">=", DelphiTokens.GreaterOrEqual);
                keysymbols.Add("<>", DelphiTokens.NotEqual);
                keysymbols.Add(":=", DelphiTokens.Assignment);
                keysymbols.Add(".", DelphiTokens.Dot);
                keysymbols.Add("..", DelphiTokens.Interval);
                keysymbols.Add(":", DelphiTokens.Colon);
                keysymbols.Add(",", DelphiTokens.Comma);
                keysymbols.Add(";", DelphiTokens.Semicolon);
                keysymbols.Add("'", DelphiTokens.String);
                keysymbols.Add("[", DelphiTokens.LSBr);
                keysymbols.Add("]", DelphiTokens.RSBr);
                keysymbols.Add("(", DelphiTokens.LBr);
                keysymbols.Add(")", DelphiTokens.RBr);
                keysymbols.Add("{", DelphiTokens.LFBr);
                keysymbols.Add("}", DelphiTokens.RFBr);
                keysymbols.Add("/*", DelphiTokens.LCBr);
                keysymbols.Add("*/", DelphiTokens.RCBr);
                keysymbols.Add("(*", DelphiTokens.LRBr);
                keysymbols.Add("*)", DelphiTokens.RRBr);
                keysymbols.Add("//", DelphiTokens.LDBr);
                keysymbols.Add("@", DelphiTokens.AtSign);
                keysymbols.Add("^", DelphiTokens.Caret);
                keysymbols.Add("\n", DelphiTokens.NewLine);
                keysymbols.Add("$", DelphiTokens.HexConstant);
                keysymbols.Add("#", DelphiTokens.Char);
            }
        }

        private void InitFileMap()
        {
            if (filemap == null)
                filemap = new FileMap();
            else filemap.Reset();
        }

        /// <summary>
        /// Инициализация анализатора
        /// </summary>
        private void Initialize(string file, string source)
        {
            InitKeyWords();
            InitKeySymbols();
            InitFileMap();
            this.file = file;
            index = 0;
            token = DelphiTokens.Undefined;
            this.source = source;
            currentId = string.Empty;
            metatags = new SafeDictionary<string, string>((x) => null);
            dfmids = new List<string>();
            tokens = new List<TokenInfo>();
        }

        /// <summary>
        /// Чтение одного токена
        /// </summary>
        private int Scan()
        {
            int start = index;
            token = DelphiTokens.Undefined;
            currentId = string.Empty;
            if (index >= source.Length)
            {
                token = DelphiTokens.FileEnd;
                return start;
            }
            char c;
            int state = 1;
            while (index < source.Length)
            {
                c = source[index];
                switch (state)
                {
                    case 1:         // определяющее состояние
                        if (DChar.IsLetter(c) || (c == '_'))
                        {
                            currentId += c;
                            token = DelphiTokens.Id;
                            state = 2;
                        }
                        else if (DChar.IsDigit(c))
                        {
                            currentId += c;
                            token = DelphiTokens.Digits;
                            state = 3;
                        }
                        else
                        {
                            switch (c)
                            {
                                case '/':
                                    currentId += c;
                                    token = DelphiTokens.Division;
                                    state = 4;
                                    break;
                                case '>':
                                    currentId += c;
                                    token = DelphiTokens.Greater;
                                    state = 8;
                                    break;
                                case '<':
                                    currentId += c;
                                    token = DelphiTokens.Less;
                                    state = 9;
                                    break;
                                case '.':
                                    currentId += c;
                                    token = DelphiTokens.Dot;
                                    state = 10;
                                    break;
                                case ':':
                                    currentId += c;
                                    token = DelphiTokens.Colon;
                                    state = 11;
                                    break;
                                case '$':
                                    currentId += c;
                                    state = 12;
                                    break;
                                case '#':
                                    currentId += c;
                                    state = 14;
                                    break;
                                case '\'':
                                    currentId += c;
                                    state = 16;
                                    break;
                                case '(':
                                    currentId += c;
                                    token = DelphiTokens.LBr;
                                    state = 17;
                                    break;
                                case '{':
                                    currentId += c;
                                    token = DelphiTokens.OComment;
                                    state = 20;
                                    break;
                                case '*':
                                case '+':
                                case '-':
                                case '=':
                                case ',':
                                case ';':
                                case ')':
                                case '[':
                                case ']':
                                case '}':
                                case '@':
                                case '^':
                                    currentId += c;
                                    index++;
                                    token = keysymbols[c.ToString()];
                                    return start;
                                case '\n':
                                    char c3 = source[index];
                                    try
                                    {
                                        char c2 = source[index + 1];
                                        if (c2 == '\n') filemap.EmptyStrings.Add(index);
                                    }
                                    catch { }

                                    start++;
                                    filemap.AddLine(index + 1);
                                    break;
                                default:
                                    start++;
                                    break;
                            }
                        }
                        break;
                    case 2:     // идентификатор
                        if (DChar.IsLetterOrDigit(c) || (c == '_')) currentId += c;
                        else return start;
                        break;
                    case 3:     // число (может оказаться вещественным или заданным в экспоненциальной форме)
                        if (DChar.IsDigit(c)) currentId += c;
                        else if ((c == '.') && (source[index + 1] != '.'))
                        {
                            currentId += c;
                            token = DelphiTokens.Real;
                            state = 23;
                        }
                        else if ((c == 'e') || (c == 'E'))
                        {
                            currentId += c;
                            token = DelphiTokens.Exponent;
                            state = 24;
                        }
                        else return start;
                        break;
                    case 4:     // знак деления (может оказаться комментарием типа // или /* ... */)
                        switch (c)
                        {
                            case '/':
                                currentId += c;
                                token = DelphiTokens.LComment;
                                state = 5;
                                break;
                            case '*':
                                currentId += c;
                                token = DelphiTokens.MLComment;
                                state = 6;
                                break;
                            default:
                                return start;
                        }
                        break;
                    case 5:     // комментарий типа //
                        if (c == '\n')
                        {
                            filemap.AddLine(index + 1);
                            return start;
                        }
                        else currentId += c;
                        break;
                    case 6:     // комментарий типа /* ... */
                        if (c == '*')
                        {
                            currentId += c;
                            state = 7;
                        }
                        else
                        {
                            if (c == '\n') filemap.AddLine(index + 1);
                            currentId += c;
                        }
                        break;
                    case 7:     // окончание комментария типа /* ... */
                        if (c == '/')
                        {
                            currentId += c;
                            index++;
                            return start;
                        }
                        else
                        {
                            currentId += '*';
                            if (c != '*')
                            {
                                if (c == '\n') filemap.AddLine(index + 1);
                                currentId += c;
                                state = 6;
                            }
                        }
                        break;
                    case 8:     // знак "больше" (может оказаться знаком "больше или равно")
                        if (c == '=')
                        {
                            currentId += c;
                            token = DelphiTokens.GreaterOrEqual;
                            index++;
                        }
                        return start;
                    case 9:     // знак "меньше" (может оказаться знаками "меньше или равно" или "не равно")
                        switch (c)
                        {
                            case '=':
                                currentId += c;
                                token = DelphiTokens.LessOrEqual;
                                index++;
                                break;
                            case '>':
                                currentId += c;
                                token = DelphiTokens.NotEqual;
                                index++;
                                return start;
                        }
                        return start;
                    case 10:    // точка (может оказаться интервалом)
                        if (c == '.')
                        {
                            currentId += c;
                            token = DelphiTokens.Interval;
                            index++;
                        }
                        return start;
                    case 11:    // двоеточие (может оказаться оператором присвоения)
                        if (c == '=')
                        {
                            currentId += c;
                            token = DelphiTokens.Assignment;
                            index++;
                        }
                        return start;
                    case 12:    // числовая константа в шестнадцатиричном формате
                        if (DChar.IsHexSymbol(c))
                        {
                            currentId += c;
                            token = DelphiTokens.HexConstant;
                            state = 13;
                        }
                        break;
                    case 13:    // числовая константа в шестнадцатиричном формате
                        if (DChar.IsHexSymbol(c)) currentId += c;
                        else return start;
                        break;
                    case 14:    // код символа (например, #13; может оказаться заданным в шестнадцатиричной форме)
                        if (DChar.IsDigit(c))
                        {
                            currentId += c;
                            token = DelphiTokens.Char;
                            state = 15;
                        }
                        else if (c == '$')
                        {
                            currentId += c;
                            state = 21;
                        }
                        else return start;
                        break;
                    case 15:    // код символа
                        if (DChar.IsDigit(c)) currentId += c;
                        else return start;
                        break;
                    case 16:    // строка
                        if (c == '\'')
                        {
                            currentId += c;
                            token = (currentId.Length == 1) ? DelphiTokens.Char : DelphiTokens.String;
                            index++;
                            return start;
                        }
                        else if (c == '\n') return start;
                        else currentId += c;
                        break;
                    case 17:    // круглая открывающая скобка (может оказаться комментарием типа (* ... *))
                        if (c == '*')
                        {
                            currentId += c;
                            token = DelphiTokens.BComment;
                            state = 18;
                        }
                        else return start;
                        break;
                    case 18:    // комментарий в круглых скобках: (* ... *)
                        if (c == '*')
                        {
                            currentId += c;
                            state = 19;
                        }
                        else
                        {
                            if (c == '\n') filemap.AddLine(index + 1);
                            currentId += c;
                        }
                        break;
                    case 19:    // комментарий в круглых скобках: (* ... *)
                        if (c == ')')
                        {
                            currentId += c;
                            index++;
                            return start;
                        }
                        else
                        {
                            if (c != '*')
                            {
                                currentId += c;
                                if (c == '\n') filemap.AddLine(index + 1);
                                state = 18;
                            }
                        }
                        break;
                    case 20:    // комментарий в фигурных скобках: { ... }
                        if (c == '}')
                        {
                            currentId += c;
                            index++;
                            return start;
                        }
                        else
                        {
                            if (c == '\n') filemap.AddLine(index + 1);
                            currentId += c;
                        }
                        break;
                    case 21:    // код символа, заданный шестнадцатиричной константой (например, #$0A)
                        if (DChar.IsHexSymbol(c))
                        {
                            currentId += c;
                            token = DelphiTokens.Char;
                            state = 22;
                        }
                        else return start;
                        break;
                    case 22:    // код символа, заданный шестнадцатиричной константой
                        if (DChar.IsHexSymbol(c)) currentId += c;
                        else return start;
                        break;
                    case 23:    // вещественное число с плавающей точкой
                        if (DChar.IsDigit(c))
                            currentId += c;
                        else if ((c == 'e') || (c == 'E'))
                        {
                            currentId += c;
                            token = DelphiTokens.Exponent;
                            state = 24;
                        }
                        else return start;
                        break;
                    case 24:    // вещественное число в экспоненциальном форме (положительная или отрицательная степень экспоненты)
                        if ((c == '-') || (c == '+'))
                            currentId += c;
                        else index--;
                        state = 25;
                        break;
                    case 25:    // вещественное число в экспоненциальном форме (степень экспоненты)
                        if (DChar.IsDigit(c))
                            currentId += c;
                        else return start;
                        break;
                }
                index++;
            }
            return start;
        }

        /// <summary>
        /// Чтение asm-кода до токена end
        /// </summary>
        /// <returns>токен-ассемблерный код</returns>
        private TokenInfo GetAsmCode()
        {
            int start = index, endstart = index;
            string temp = string.Empty;
            currentId = string.Empty;
            if (index >= source.Length)
                return new TokenInfo(DelphiTokens.FileEnd, string.Empty, file, filemap.Line(index), metatags, dfmids);
            char c;
            int state = 1;
            while (index < source.Length)
            {
                c = source[index];
                if (DChar.IsSeparator(c))
                {
                    currentId += c;
                    state = 2;
                    index++;
                    continue;
                }
                switch (state)
                {
                    case 2:
                        if ((c == 'e') || (c == 'E'))
                        {
                            endstart = index;
                            temp = c.ToString();
                            state = 3;
                        }
                        else
                        {
                            currentId += c;
                            state = 1;
                        }
                        break;
                    case 3:
                        if ((c == 'n') || (c == 'N'))
                        {
                            temp += c;
                            state = 4;
                        }
                        else
                        {
                            currentId += temp;
                            state = 1;
                        }
                        break;
                    case 4:
                        if ((c == 'd') || (c == 'D'))
                        {
                            temp += c;
                            state = 5;
                        }
                        else
                        {
                            currentId += temp;
                            state = 1;
                        }
                        break;
                    case 5:
                        if (!DChar.IsLetter(c) && !DChar.IsDigit(c) && (c != '_'))
                        {
                            index = endstart;
                            return new TokenInfo(DelphiTokens.AsmCode, currentId, file, filemap.Line(start), metatags, dfmids);
                        }
                        else
                        {
                            currentId += temp;
                            start = 1;
                        }
                        break;
                }
                index++;
            }
            return new TokenInfo(DelphiTokens.FileEnd, string.Empty, file, filemap.Line(index), metatags, dfmids);
        }

        /// <summary>
        /// Считывание токена с игнорирование комментариев
        /// </summary>
        private TokenInfo FullScan()
        {
            int line = filemap.Line(Scan());
            string lowerId = currentId.ToLower();
            if ((token == DelphiTokens.Id) && keywords.ContainsKey(lowerId))
                token = keywords[lowerId];
            switch (token)
            {
                case DelphiTokens.LComment:
                case DelphiTokens.MLComment:
                case DelphiTokens.OComment:
                case DelphiTokens.BComment:
                    if (token == DelphiTokens.OComment)
                    {
                        Match match = dfm.Match(currentId);
                        if (match.Success)
                        {
                            string filename = (match.Groups["dfm"].Captures[0].Value == "*") ? file.Substring(0, file.LastIndexOf(".")) : match.Groups["dfm"].Captures[0].Value;
                            filename = string.Format("{0}.dfm", filename);
                            if (File.Exists(filename))
                                dfmids.AddRange(DfmSyntaxAnalyser.Instance.Translate(filename, File.ReadAllText(filename), metatags));
                        }
                    }
                    MatchCollection tags = meta.Matches(currentId);
                    if (tags.Count > 0)
                    {
                        string key = string.Empty;
                        foreach (Match tag in tags)
                        {
                            if (tag.Groups["tag"].Captures.Count > 0)
                            {
                                key = tag.Groups["tag"].Captures[0].Value.ToLower();
                                if (!metatags.ContainsKey(key))
                                    metatags[key] = tag.Groups["value"].Captures[0].Value;
                            }
                        }
                    }
                    break;
            }
            if (token == DelphiTokens.Asm)
            {
                tokens.Add(new TokenInfo(token, currentId, this.file, line, metatags, dfmids));
                tokens.Add(GetAsmCode());
                return FullScan();
            }
            return new TokenInfo(token, currentId, this.file, line, metatags, dfmids);
        }

        /// <summary>
        /// Выделение строки кода, содержащей заданную позицию
        /// </summary>
        /// <param name="Source">исходный код</param>
        /// <param name="Index">позиция</param>
        /// <param name="Map">карта исходного кода</param>
        /// <returns></returns>
        private string ExtractLine()
        {
            int Line = filemap.Line(index),
                Start = filemap.Index(Line),
                Length = filemap.Index(Line + 1) - Start;
            return source.Substring(Start, Length);
        }

        /// <summary>
        /// Трансляция исходного кода
        /// </summary>
        /// <param name="source">транслируемый исходный код</param>
        /// <returns>список токенов</returns>
        public List<TokenInfo> Translate(string file, string source)
        {
            try
            {
                
                Initialize(file, source);
                tokens.Add(new TokenInfo(DelphiTokens.Undefined, string.Empty, this.file, 0, metatags, dfmids));
                while (token != DelphiTokens.FileEnd)
                    tokens.Add(FullScan());

                FileSLOC.Add(file, filemap.Last);
                FileEmptyLiles.Add(file, filemap.EmptyStrings.Count);
                if (FileAnalysedEvent != null)
                    FileAnalysedEvent(this, new SourceArgs(file, filemap, metatags, source));
                return tokens;
            }
            catch (Exception e)
            {
                if (e is DanalyserError)
                    AddError(filemap.Line(index), ExtractLine(), metatags);
                else AddUndefinedError(file, tokens[tokens.Count - 1].Line, tokens[tokens.Count - 1].Text, tokens[tokens.Count - 1].MetaTags);
            }
            return tokens;
        }

        /// <summary>
        /// Единственный экземпляр лексического анализатора
        /// </summary>
        public static LexicalAnalyser Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new LexicalAnalyser();
                    FileSLOC = new Dictionary<string, int>();
                    FileEmptyLiles = new Dictionary<string, int>();
                }
                return instance;
            }
        }
    }
}
